//////////////////////////////////////////////////////////////////////////////
//       Copyright (c) 2003 - RealTime Gaming.  All rights reserved.        //
//////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////
// Includes
//////////////////////////////////////////////////////////////////////////////
#include "StrTableMgr.h"
#include "libglob.h"

//////////////////////////////////////////////////////////////////////////////
// Namespaces
//////////////////////////////////////////////////////////////////////////////
using namespace Library;

//////////////////////////////////////////////////////////////////////////////
/// Default Constructor
//////////////////////////////////////////////////////////////////////////////
StrTableMgr::StrTableMgr( KeywordSubstitutor* pSubstitutor, LocaleFormatter* pFormatter ) 
    : m_plistTables(NULL),
      m_pSubstitutor(pSubstitutor), 
      m_pFormatter(pFormatter)

{
	m_plistTables = new List<StrTable*>();
    
    ASSERT( m_plistTables );
    ASSERT( m_pSubstitutor );
    ASSERT( m_pFormatter );
}

//////////////////////////////////////////////////////////////////////////////
/// Destructor
//////////////////////////////////////////////////////////////////////////////
StrTableMgr::~StrTableMgr()
{
	// Destroy all StringTables in the hash table

	// Scope this so the iterator goes out of scope before we destroy the
	//  table.  This is the only place we should really have to do this.
	{
		ListIterator<StrTable*> it(m_plistTables);

		while( it.hasNext() )
		{
			// NOTE: You should call "ReleaseTable" on your string table
			//        as modules go out of scope
			ASSERT(false);

			// However, we will cleanup the memory anyway!
			StrTable* pTable = it.next();
			delete pTable;
		}
	}

	// Cleanup memory
	delete m_plistTables;
}

//////////////////////////////////////////////////////////////////////////////
/// @return				TRUE if successful
///
/// @param sTableName	This is the string table name as stored in the
///                     resource file.
///
/// This method will load a string table from the application resources. You 
///  may call this method on a string table that is already loaded.  If you
///  do so, the reference count will simply be increased.
//////////////////////////////////////////////////////////////////////////////
bool8 
StrTableMgr::LoadTableFromResource( const String& sTableName )
{
	bool8 bRetVal = false;

	// First, let's see if this string table is already loaded
	StrTable* pTable = FindTable( sTableName );

	// If it has been loaded already, just update the ref count and end
	if ( pTable )
	{
		// Update the ref count
		pTable->AddRef();

		// We can return true now
		bRetVal = true;
	}
	else
	{
		// Create a new table
		pTable = new StrTable( m_pSubstitutor, m_pFormatter );
		
		// Load from the resource
		bRetVal = pTable->LoadFromResource( sTableName );
		
		// Check and see if the load succeeded
		if ( bRetVal )
		{
			// Add a ref count to the string table
			pTable->AddRef();
			
			// Add the string table to our hash table
			m_plistTables->add( pTable );
		}
		else
		{
			delete pTable;
		}
	}

	return bRetVal;
}

//////////////////////////////////////////////////////////////////////////////
/// @return				TRUE if successful.
///
/// @param sTableName	This is string name of the new table.
///
/// This method will attempt to create a new, empty string table with the
///  given name and add it to the list in the string table manager.  This
///  method will fail if a table already exists with the same name.
//////////////////////////////////////////////////////////////////////////////
bool8 
StrTableMgr::CreateTable( const String& sTableName )
{
	bool8 bRetVal = false;
	
	// We do not allow mutiple tables with the same name
	if ( !FindTable( sTableName ) )
	{
		// Create a new table
		StrTable* pTable = new StrTable( m_pSubstitutor, m_pFormatter );
		
		// Set the name of the new table
		pTable->SetName( sTableName );
		
		// Add a ref count to the string table
		pTable->AddRef();
		
		// Add the string table to our hash table
		m_plistTables->add( pTable );
		
		// Return success
		bRetVal = true;
	}
	
	return bRetVal;
}

//////////////////////////////////////////////////////////////////////////////
/// @return				TRUE if the table was found (i.e. was loaded).
///
/// @param sTableName	This is the string table name as stored in the
///                     resource file - without a ".str" extension.
///
/// Call this method when you are done with a particular string table - such
///  as when a game is unloaded.  This method will free the string table in
///  memory if no one is using it anymore. 
//////////////////////////////////////////////////////////////////////////////
bool8 
StrTableMgr::ReleaseTable( const String& sTableName )
{
	bool8 bRetVal = false;

	// Find the table
	StrTable* pTable = FindTable( sTableName );
	if ( pTable )
	{
		// Update the ref count
		pTable->Release();

		// See if we are still using this table
		if ( pTable->GetRefCount() == 0 )
		{
			// remove the string table from the hash table
			m_plistTables->remove( pTable );

			// Delete the string table
			delete pTable;
		}

		// we can return true now
		bRetVal = true;
	}

	return bRetVal;
}

//////////////////////////////////////////////////////////////////////////////
/// @return				TRUE if the table was found (i.e. was loaded).
///
/// @param sTableName	This is the string table name as stored in the
///                     resource file - without a ".str" extension.
///
/// Clears the contents of the table.
//////////////////////////////////////////////////////////////////////////////
bool8 
StrTableMgr::ClearTable( const String& sTableName )
{
    bool8 bRetVal = false;

	// Find the table
	StrTable* pTable = FindTable( sTableName );
	if ( pTable )
	{
		// Clear the table
		pTable->Clear();

		// We found the table
		bRetVal = true;
	}

	return bRetVal;
}

//////////////////////////////////////////////////////////////////////////////
/// @return				String reference, will be empty string if not found.
///
/// @param szShortName	Key (shortname) of the string to locate in the table.
///
/// This method will search all currently loaded string tables for a string 
///   and return a pointer to that string object if found.  
//////////////////////////////////////////////////////////////////////////////
const String& 
StrTableMgr::GetString( const String& sShortName )
{
	// Create an iterator to loop through all the currently loaded 
	//  string tables.
	ListIterator<StrTable*> it(m_plistTables);

	// Put the iterator at the end of the list
	it.end();

	// Walk the list backwards, so support the idea that last loaded string
	//  table gets the first search priority.
	while( it.hasPrevious() )
	{
		// Get each string table
		StrTable* pTable = it.previous();

		// See if the key passed in is found in the string table
		if (!GetStringWithSubstition( pTable, sShortName ).isEmpty())
		{
			return GetStringWithSubstition( pTable, sShortName );
		}
	}

    // Display a debug message if the string is not found
    DEBUGMSG(("WARNING: GetString(\"%S\") returning empty string!", (const uint16*) sShortName) );

	// If string not found, return empty string
	return m_sEmpty;
}

//////////////////////////////////////////////////////////////////////////////
/// @return				String reference, will be empty string if not found.
///
/// @param sTableName	The name of the string table in which to search.
///
/// @param sShortName	Key (shortname) of the string to locate in the table.
///
/// This method will search the string table for a string and return a pointer
/// to that string object if found.  
//////////////////////////////////////////////////////////////////////////////
const String&
StrTableMgr::GetString( const String& sTableName, const String& sShortName )
{
	// Find the table
	StrTable* pTable = FindTable( sTableName );
	if ( pTable )
	{
		// Get the string
		return( GetStringWithSubstition( pTable, sShortName ) );
	}

    // Display a debug message if the string is not found
    DEBUGMSG(("WARNING: GetString(\"%S\",\"%S\") returning empty string!", (const uint16*) sTableName, (const uint16*) sShortName) );

    // Return empty string if not found
	return m_sEmpty;
}

//////////////////////////////////////////////////////////////////////////////
/// @return				True if the string was successfully added.
///
/// @param sTableName	The name of the string table to which to add the string.
///
/// @param sShortName	The key (shortname) of the new string.
///
/// @param string		The actual string to be added.
///
/// This method will add a string to a given string table.  The string must
///  not already exist in that table, and the function will return false and
///  fail if it does.
//////////////////////////////////////////////////////////////////////////////
bool8 
StrTableMgr::AddString( const String& sTableName, const String& sShortName, const String& string )
{
	bool8 bRetVal = false;

	StrTable* pTable = FindTable( sTableName );
	if ( pTable )
	{
		bRetVal = pTable->AddString( sShortName, string );
	}

	return bRetVal;
}

//////////////////////////////////////////////////////////////////////////////
/// @return				True if the string was successfully removed.
///
/// @param sTableName	The name of the string table.
///
/// @param sShortName	The key (shortname) of the string to be removed.
///
/// This method will remove a string from a string table.
//////////////////////////////////////////////////////////////////////////////
bool8 
StrTableMgr::RemoveString( const String& sTableName, const String& sShortName )
{
	bool8 bRetVal = false;

	StrTable* pTable = FindTable( sTableName );
	if ( pTable )
	{
		bRetVal = pTable->RemoveString( sShortName );
	}

	return bRetVal;
}

//////////////////////////////////////////////////////////////////////////////
/// @return				Pointer to a string table, NULL if not found.
///
/// @param sTableName	The name of the string table.
///
/// This is a helper method used to search the internal linked list for a
///  specific string table.
//////////////////////////////////////////////////////////////////////////////
StrTable* 
StrTableMgr::FindTable( const String& sTableName )
{
	StrTable* pRetVal = NULL;
	
	// Create an iterator to loop through all the currently loaded 
	//  string tables.
	ListIterator<StrTable*> it(m_plistTables);

	// Put the iterator at the end of the list
	it.end();

	// Walk the list backwards, to support the idea that last loaded string
	//  table gets the first search priority.
	while( it.hasPrevious() && !pRetVal )
	{
		// Get each string table
		StrTable* pTable = it.previous();

		// Check name
		if ( pTable->GetName() == sTableName )
		{
			// Set return
			pRetVal = pTable;
		}
	}

	return pRetVal;
}

//////////////////////////////////////////////////////////////////////////////
/// @return				String reference, will be empty string if not found.
///
/// @param pTable		Table from which to retreive the string.
///
/// @param sShortName   Key (shortname) of the string in the table.
///
/// This is a helper method used to get a string from a string table and
///  perform keyword substition.
//////////////////////////////////////////////////////////////////////////////

const String&
StrTableMgr::GetStringWithSubstition( const StrTable* pTable, const String& sShortName )
{
	const String& s = pTable->GetString( sShortName );

	// If the string is found, go ahead and perform keyword substitution as
	//  needed.
	if ( m_pSubstitutor && !s.isEmpty() )
	{
        // Make sure the cachename is unique
		String CacheName = pTable->GetName();
        CacheName += sShortName;

		return ( m_pSubstitutor->PerformSubstitution( CacheName, s ) );
	}

	return s;
}